
package com.jcl.core.ftp;

import com.jcl.core.exception.FtpException;
import com.jcl.core.utils.StringUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class FtpProxy {

    /**
     * Logger for this class
     */
    private static final Logger logger        = LoggerFactory
                                                        .getLogger(FtpProxy.class);

    protected static final String FTP_HEAD                = "ftp://";

    private static final String   DOT_DOUBLE              = "..";

    private static final String   GBK                     = "gbk";

    private static final String   ISO8859                 = "ISO8859-1";

    protected FTPClient           ftpClient;

    protected FtpConfig           ftpConfig;

    // public static final int BINARY_FILE_TYPE = FTP.BINARY_FILE_TYPE;

    // public static final int ASCII_FILE_TYPE = FTP.ASCII_FILE_TYPE;

    // public static final int CONNECT_TIME_OUT = 3000;

    public static final int       DATA_TIME_OUT = 60000;

    public FTPClient getFtpClient() {

        return ftpClient;
    }

    public void setFtpClient(FTPClient ftpClient) {

        this.ftpClient = ftpClient;
    }

    // path should not the path from root index
    // or some FTP server would go to root as '/'.
    public FtpProxy(FtpConfig ftpConfig) throws IOException {

        this.ftpConfig = ftpConfig;
        connectServer();
    }

    public FtpProxy(String userName, String password, String path)
            throws IOException {

        FtpConfig ftpConfig = createFtpConfig(userName, password, path);
        this.ftpConfig = ftpConfig;
        connectServer();

    }

    protected FtpConfig createFtpConfig(String userName,
                                        String password,
                                        String path) {

        String server = null;
        Integer port = null;
        String location = null;
        if (path.toLowerCase().startsWith(FTP_HEAD)) {
            String str = path.substring(6);
            int index = str.indexOf(StringUtils.DIAGONAL);
            String serverPort = null;
            if (index > 0) {
                serverPort = str.substring(0, index);
                location = str.substring(index);
            } else {
                serverPort = str;
                location = StringUtils.DIAGONAL;
            }

            index = serverPort.indexOf(StringUtils.COLON);
            if (index > 0) {
                server = serverPort.substring(0, index);
                port = Integer.valueOf(serverPort.substring(index + 1));
            } else {
                server = serverPort;
            }
        }

        FtpConfig config = new FtpConfig();
        config.setServer(server);
        if (port != null) {
            config.setPort(port);
        }
        config.setUserName(userName);
        config.setPassword(password);
        config.setLocation(location);

        return config;
    }

    public void connectServer() throws IOException {

        if (logger.isDebugEnabled()) {
            logger.debug("登录FTP服务器：" + ftpConfig);
        }

        String server = ftpConfig.getServer();
        Integer port = ftpConfig.getPort();
        if (port == null) {
            port = FTP.DEFAULT_PORT;
        }
        String user = ftpConfig.getUserName();
        String password = ftpConfig.getPassword();
        String location = ftpConfig.getLocation();

        ftpClient = new FTPClient();
        //ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
        // ftpClient.setConnectTimeout(CONNECT_TIME_OUT);
        ftpClient.setDataTimeout(DATA_TIME_OUT);
        ftpClient.setControlEncoding("UTF-8");
        ftpClient.connect(server, port);

        int reply = ftpClient.getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply)) {
            ftpClient.disconnect();
            throw new FtpException("FTP server refused connection:" + reply);
        }

        if (!ftpClient.login(user, password)) {
            throw new FtpException("FTP server refused login.");
        }

        Boolean binaryTransfer = ftpConfig.isBinaryTransfer();
        if (binaryTransfer != null && binaryTransfer) {
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        }
        ftpClient.enterLocalPassiveMode();

        if (StringUtils.hasLength(location)) {
            changeDirectory(location);
        }

    }

    protected String getWorkingDirectory(String remoteFilePath) {

        if (!remoteFilePath.startsWith(StringUtils.DIAGONAL)) {
            remoteFilePath = StringUtils.DIAGONAL + remoteFilePath;
        }
        return getRemoteFilePath(remoteFilePath);

    }

    protected String getRemoteFilePath(String remoteFilePath) {

        try {
            return new String(remoteFilePath.getBytes(GBK), ISO8859);
        } catch (UnsupportedEncodingException e) {
            return remoteFilePath;
        }

    }

    // FTP.BINARY_FILE_TYPE | FTP.ASCII_FILE_TYPE
    // Set transform type
    public void setFileType(int fileType) throws IOException {

        ftpClient.setFileType(fileType);
    }

    public void closeServer() {

        if (logger.isDebugEnabled()) {
            logger.debug("关闭FTP链接" + ftpConfig);
        }
        if (ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
                logger.error(e.getMessage(), e);
            }
        }
    }

    public boolean changeDirectory(String path) throws IOException {

        return ftpClient.changeWorkingDirectory(getWorkingDirectory(path));

    }

    public boolean createDirectory(String pathName) throws IOException {

        return ftpClient.makeDirectory(getWorkingDirectory(pathName));

    }

    public boolean removeDirectory(String path) throws IOException {

        return ftpClient.removeDirectory(getWorkingDirectory(path));

    }

    public boolean removeDirectory(String path, boolean isAll)
            throws IOException {

        if (!isAll) {
            return removeDirectory(path);
        }

        FTPFile[] ftpFileArr = ftpClient.listFiles(getWorkingDirectory(path));
        if (ftpFileArr == null || ftpFileArr.length == 0) {
            return removeDirectory(path);
        }

        for (FTPFile ftpFile : ftpFileArr) {
            String name = ftpFile.getName();
            if (ftpFile.isDirectory()) {

                if (!name.equals(StringUtils.DOT) && (!name.equals(DOT_DOUBLE))) {
                    removeDirectory(path + StringUtils.DIAGONAL + name, true);
                }

            } else if (ftpFile.isFile()) {

                deleteFile(path + StringUtils.DIAGONAL + name);
            } else if (ftpFile.isSymbolicLink()) {

            } else if (ftpFile.isUnknown()) {

            }
        }
        return removeDirectory(path);

    }

    public boolean existDirectory(String path) throws IOException {

        boolean flag = false;
        FTPFile[] ftpFileArr = ftpClient.listFiles(getWorkingDirectory(path));
        for (FTPFile ftpFile : ftpFileArr) {
            if (ftpFile.isDirectory() && ftpFile.getName()
                        .equalsIgnoreCase(path)) {
                flag = true;
                break;
            }
        }
        return flag;

    }

    public boolean existFile(String path, String fileName) throws IOException {
        boolean flag = false;
        FTPFile[] ftpFileArr = ftpClient.listFiles(getWorkingDirectory(path));
        for (FTPFile ftpFile : ftpFileArr) {
            if (ftpFile.isFile() && ftpFile.getName().equals(fileName)) {
                flag = true;
                break;
            }
        }
        return flag;
    }
    
    public boolean existFile(String path) throws IOException {

        boolean flag = false;
        FTPFile[] ftpFileArr = ftpClient.listFiles(getWorkingDirectory(path));
        for (FTPFile ftpFile : ftpFileArr) {
            if (ftpFile.isFile() && ftpFile.getName().equalsIgnoreCase(path)) {
                flag = true;
                break;
            }
        }
        return flag;

    }

    public List<FTPFile> getFileList(String path) throws IOException {

        FTPFile[] ftpFiles = ftpClient.listFiles(getWorkingDirectory(path));

        List<FTPFile> retList = new ArrayList<FTPFile>();
        if (ftpFiles == null || ftpFiles.length == 0) {
            return retList;
        }
        for (FTPFile ftpFile : ftpFiles) {
            if (ftpFile.isFile()) {
                retList.add(ftpFile);
            }
        }
        return retList;

    }

    public List<FTPFile> getFileListAfterTime(String path, Date timestamp)
            throws IOException {

        FTPFile[] ftpFiles = ftpClient.listFiles(getWorkingDirectory(path));

        List<FTPFile> retList = new ArrayList<FTPFile>();
        if (ftpFiles == null || ftpFiles.length == 0) {
            return retList;
        }
        for (FTPFile ftpFile : ftpFiles) {
            if (ftpFile.isFile()) {
                if (timestamp != null) {
                    Calendar time = ftpFile.getTimestamp();
                    if (!time.before(timestamp)) {
                        retList.add(ftpFile);
                    }
                } else {
                    retList.add(ftpFile);
                }

            }
        }
        return retList;

    }

    public List<FTPFile> getFolderListAfterTime(String path, Date timestamp)
            throws IOException {

        FTPFile[] ftpFiles = ftpClient.listFiles(getWorkingDirectory(path));

        List<FTPFile> retList = new ArrayList<FTPFile>();
        if (ftpFiles == null || ftpFiles.length == 0) {
            return retList;
        }
        for (FTPFile ftpFile : ftpFiles) {
            if (ftpFile.isDirectory()) {
                String name = ftpFile.getName();
                if (!name.equals(StringUtils.DOT) && (!name.equals(DOT_DOUBLE))) {

                    if (timestamp != null) {
                        Calendar time = ftpFile.getTimestamp();

                        if (!time.getTime().before(timestamp)) {
                            retList.add(ftpFile);
                        }
                    } else {
                        retList.add(ftpFile);
                    }
                }
            }
        }
        return retList;

    }

    public List<FTPFile> getFolderListAfterTime(Date timestamp)
            throws IOException {

        FTPFile[] ftpFiles = ftpClient.listFiles();

        List<FTPFile> retList = new ArrayList<FTPFile>();
        if (ftpFiles == null || ftpFiles.length == 0) {
            return retList;
        }
        for (FTPFile ftpFile : ftpFiles) {

            if (ftpFile.isDirectory()) {
                String name = ftpFile.getName();
                if (!name.equals(StringUtils.DOT) && (!name.equals(DOT_DOUBLE))) {
                    if (timestamp != null) {
                        Calendar time = ftpFile.getTimestamp();

                        if (!time.getTime().before(timestamp)) {
                            retList.add(ftpFile);
                        }
                    } else {
                        retList.add(ftpFile);
                    }
                }

            }
        }
        return retList;

    }

    public boolean deleteFile(String pathName) throws IOException {

        return ftpClient.deleteFile(getRemoteFilePath(pathName));

    }

    public boolean uploadFile(String fileName, String newName)
            throws IOException {

        InputStream iStream = null;
        try {
            iStream = new FileInputStream(fileName);
            return uploadFile(iStream, newName);
        } finally {
            IOUtils.closeQuietly(iStream);
        }
    }

    public boolean uploadFile(String fileName) throws IOException {

        return uploadFile(fileName, fileName);
    }

    public boolean uploadFile(InputStream iStream, String newName)
            throws IOException {

        try {
            // ftpClient.setControlEncoding("GBK");
            // ftpClient.setBufferSize(1024);
            // 设置文件类型（二进制）
            // ftpClient.setFileType(BINARY_FILE_TYPE);
            return ftpClient.storeFile(getRemoteFilePath(newName), iStream);

        } finally {
            IOUtils.closeQuietly(iStream);
        }

    }

    public boolean storeFile(byte[] content, String newName) throws IOException {

        newName = getRemoteFilePath(newName);

        int index = newName.lastIndexOf(StringUtils.DIAGONAL);
        if (index > 0) {
            String folder = newName.substring(0, index);
            String[] array = folder.split(StringUtils.DIAGONAL);
            StringBuffer buffer = new StringBuffer();
            if (buffer.length() == 0) {
                buffer.append(StringUtils.DIAGONAL);
            }
            for (String path : array) {
                if (StringUtils.hasLength(path)) {
                    buffer.append(StringUtils.DIAGONAL).append(path);
                    if (!existDirectory(path)) {
                        createDirectory(buffer.toString());
                    }
                    changeDirectory(buffer.toString());
                }
            }
        }
        InputStream iStream = null;
        try {
             ftpClient.setControlEncoding("GBK");
             ftpClient.setBufferSize(1024);
             //设置文件类型（二进制）
             ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            iStream = new ByteArrayInputStream(content);
            return ftpClient.storeFile(newName, iStream);

        } finally {
            IOUtils.closeQuietly(iStream);
        }

    }

    public boolean download(String remoteFileName, String localFileName)
            throws IOException {

        File outfile = new File(localFileName);
        OutputStream oStream = null;
        try {
            oStream = new FileOutputStream(outfile);
            return ftpClient.retrieveFile(getRemoteFilePath(remoteFileName),
                                          oStream);

        } finally {
            IOUtils.closeQuietly(oStream);
        }

    }

    public byte[] download(String remoteFileName) throws IOException {

        ByteArrayOutputStream os = null;
        try {
            os = new ByteArrayOutputStream();
            ftpClient.retrieveFile(getRemoteFilePath(remoteFileName), os);

            return os.toByteArray();

        } finally {

            IOUtils.closeQuietly(os);
        }

    }

    public String[] listFileNames() throws IOException {

        return ftpClient.listNames();

    }

    public boolean isConnected() {

        return ftpClient.isConnected();
    }

    //获取文件大小,单位:字节(B)
    public Long getFileSize(String remoteFileName){
    	Long fileSize = 0L;
    	try {
    		FTPFile[] ftpFiles = ftpClient.listFiles(getRemoteFilePath(remoteFileName));
    		if(ftpFiles.length>0){
    			for(int index=0;index<ftpFiles.length;index++){
    				if(ftpFiles[index].isFile()){
    					fileSize += ftpFiles[index].getSize();
    				}
    			}
    		}
		} catch (IOException e) {
			e.printStackTrace();
		}
    	return fileSize;
    }
}